package ro.peoplecentric.ctc.common.dao.impl;

import java.io.Serializable;
import java.util.List;
import javax.annotation.Resource;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.transform.DistinctRootEntityResultTransformer;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ro.peoplecentric.ctc.common.dao.BaseDao;

/**
 * Abstract {@link BaseDao} implementation.
 *
 * @author pguzun
 */
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public abstract class BaseDaoImpl implements BaseDao {

    @Resource
    private SessionFactory sessionFactory;

    /**
     * Get hibernate session.
     *
     * @return the current session
     */
    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    @Override
    public void flush() {
        getSession().flush();
    }

    @Override
    /**
     * USE FOR TESTS
     */
    public void clear() {
        getSession().clear();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> T getById(Class<T> clazz, Serializable id) {
        if (id == null) {
            return null;
        }
        return (T) getSession().get(clazz, id);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> List<T> getAll(Class<T> clazz) {
        Criteria criteria = getSession().createCriteria(clazz);
        criteria.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE);
        return criteria.list();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> List<T> getAll(Class<T> clazz, int firstResult, int maxResults) {
        Criteria criteria = getSession().createCriteria(clazz);
        criteria.setFirstResult(firstResult);
        criteria.setMaxResults(maxResults);
        criteria.addOrder(getDefaultOrderBy());
        criteria.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE);
        return criteria.list();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Long countAll(Class<T> clazz) {
        Criteria criteria = getSession().createCriteria(clazz);
        criteria.setProjection(Projections.rowCount());
        return (Long) criteria.uniqueResult();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(readOnly = false)
    public void saveOrUpdate(Object dataObj) {
        Session session = getSession();
        session.saveOrUpdate(dataObj);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(readOnly = false)
    public void delete(Object dataObj) {
        getSession().delete(dataObj);
    }

    /**
     * Gets default order by which fetching entity list must be sorted.
     *
     * @return Order
     */
    protected abstract Order getDefaultOrderBy();
}
